package main

import (
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

/*
Sqlx入门
题目1：使用SQL扩展库进行查询
假设你已经使用Sqlx连接到一个数据库，并且有一个 employees 表，
包含字段 id 、 name 、 department 、 salary 。
要求 ：
编写Go代码，使用Sqlx查询 employees 表中所有部门为 "技术部" 的员工信息，
并将结果映射到一个自定义的 Employee 结构体切片中。
编写Go代码，使用Sqlx查询 employees 表中工资最高的员工信息，
并将结果映射到一个 Employee 结构体中。

*/

var db *sqlx.DB

type Employee struct {
	Id         int
	Name       string
	Department string
	Salary     float64
}

// dsn := userName + ":" + password + "@tcp(localhost:3306)/?charset=utf8mb4&parseTime=True&loc=Local"
func init() {
	userName := "root"
	password := "12345678"

	dsn := userName + ":" + password + "@tcp(localhost:3306)/?charset=utf8mb4&parseTime=true"
	var err error
	db, err = sqlx.Connect("mysql", dsn) // 自动 Ping
	if err != nil {
		log.Fatalf("connect: %v", err)
	}
	db.SetMaxOpenConns(50)

	dataBaseName := "company"

	createDb(db, dataBaseName)
}

func createDb(db *sqlx.DB, dataBaseName string) error {

	var err error
	// 2. 创建数据库（如果已存在则忽略）
	if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + dataBaseName + " DEFAULT CHARACTER SET utf8mb4"); err != nil {
		log.Fatalf("create db: %v", err)
	}
	// 切换到 dataBaseName
	if _, err = db.Exec("USE " + dataBaseName); err != nil {
		log.Fatalf("use %v: %v", dataBaseName, err)
	}
	return err
}

func createTable(db *sqlx.DB, tableName string) error {
	var err error
	// 3. 创建 students 表
	ddl := fmt.Sprintf(`
	CREATE TABLE IF NOT EXISTS %v (
		id INT PRIMARY KEY,
		name VARCHAR(50) NOT NULL,
		department VARCHAR(50) NOT NULL,
		salary DECIMAL(10,2) NOT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
	`, tableName)
	if _, err := db.Exec(ddl); err != nil {
		log.Fatalf("create table: %v", err)
	}
	return err
}

func insertTable(db *sqlx.DB, tableName string, employees []Employee) error {
	// 使用命名参数进行批量插入
	sql := fmt.Sprintf(`INSERT INTO %v(id, name, department, salary) VALUES (:id, :name, :department, :salary)`, tableName)

	// 批量插入
	for _, employee := range employees {
		_, err := db.NamedExec(sql, employee)
		if err != nil {
			return fmt.Errorf("插入员工 %s 失败: %v", employee.Name, err)
		}
		log.Printf("成功插入员工: Id=%d, 姓名=%s, 部门=%s, 薪资=%.2f",
			employee.Id, employee.Name, employee.Department, employee.Salary)
	}

	return nil
}

// insertTableBatch 高效批量插入（备选方案）
func insertTableBatch(db *sqlx.DB, tableName string, employees []Employee) error {
	if len(employees) == 0 {
		return nil
	}

	// 构建批量插入SQL
	sql := fmt.Sprintf(`INSERT INTO %v(id, name, department, salary) VALUES `, tableName)
	values := []interface{}{}

	for i, employee := range employees {
		if i > 0 {
			sql += ", "
		}
		sql += "(?, ?, ?, ?)"
		values = append(values, employee.Id, employee.Name, employee.Department, employee.Salary)
	}

	_, err := db.Exec(sql, values...)
	if err != nil {
		return fmt.Errorf("批量插入员工失败: %v", err)
	}

	log.Printf("成功批量插入 %d 个员工", len(employees))
	return nil
}

func QueryEmployeesByDepartment(db *sqlx.DB, department string) ([]Employee, error) {
	var employees []Employee
	err := db.Select(&employees, "SELECT * FROM employees WHERE department = ?", department)
	if err != nil {
		return nil, err
	}
	return employees, nil
}




/*
题目2：实现类型安全映射
假设有一个 books 表，包含字段 id 、 title 、 author 、 price 。
要求 ：
定义一个 Book 结构体，包含与 books 表对应的字段。
编写Go代码，使用Sqlx执行一个复杂的查询，例如查询价格大于 50 元的书籍，并将结果映射到 Book 结构体切片中，确保类型安全。
*/

type Book struct {
    ID     int
    Title  string
    Author string
    Price  float64
}


func createBookTable(db *sqlx.DB, tableName string) error {
	var err error
	// 3. 创建 book 表
	ddl := fmt.Sprintf(`
	CREATE TABLE IF NOT EXISTS %v (
		id INT PRIMARY KEY,
		title VARCHAR(50) NOT NULL,
		author VARCHAR(50) NOT NULL,
		price DECIMAL(10,2) NOT NULL
	) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
	`, tableName)
	if _, err := db.Exec(ddl); err != nil {
		log.Fatalf("create table: %v", err)
	}
	return err
}

func insertBookTable(db *sqlx.DB, tableName string, books []Book) error {
	// 使用命名参数进行批量插入
	sql := fmt.Sprintf(`INSERT INTO %v(id, title, author, price) VALUES (:id, :title, :author, :price)`, tableName)

	// 批量插入
	for _, book := range books {
		_, err := db.NamedExec(sql, book)
		if err != nil {
			return fmt.Errorf("插入书集 %s 失败: %v", book.Title, err)
		}
		log.Printf("成功插入书集: Id=%d, 书名=%s, 作者=%s, 价格=%.2f",
			book.ID, book.Title, book.Author, book.Price)
	}

	return nil
}

func queryBookByGreatPrice(db *sqlx.DB,tableName string, price float64) ([]Book, error) {
	var books []Book
    	sql := fmt.Sprintf("SELECT * FROM %v WHERE price >= ?", tableName)
	err := db.Select(&books, sql, price)
	if err != nil {
		return nil, err
	}
	return books, nil
}